package com.talkyun.openx.server.impl;

import com.talkyun.openx.server.core.*;
import com.talkyun.utils.Reflector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

abstract class AbstractInvoker implements ServiceInvoker {
    private static final Logger logger = LoggerFactory.getLogger(AbstractInvoker.class);

    @Override
    public ServiceResponse invoke(ServiceContext ctx, ServiceRequest sr) throws Throwable {
        try {
            return this.doInvoke(ctx, sr);
        } catch (Throwable t) {
            // exec exception callback
            List<Interceptor> list = this.getInterceptor(sr.getMapping());
            // BUG-FIX list may is null!
            if (list != null && !list.isEmpty()) {
                for (Interceptor it : list) {
                    try {
                        it.onException(ctx, t);
                    } catch (Throwable t2) {
                        logger.error("Error at interceptor:" + it.getClass().getName(), t2);
                    }
                }
            }
            // throw
            throw t;
        }
    }

    private ServiceResponse doInvoke(ServiceContext ctx, ServiceRequest sr) throws Throwable {
        // -> first, run before interceptor
        List<Interceptor> list = this.getInterceptor(sr.getMapping());
        if (list != null && !list.isEmpty()) {
            for (Interceptor it : list) {
                try {
                    it.onMessageIn(ctx, sr);
                } catch (InterceptorException ie) {
                    throw ie;
                } catch (Throwable t) {
                    throw new InterceptorException(it.getClass().getName(), t);
                }
            }
        }

        // -> second, really to invoke service
        ServiceResponse sp = this.doInvoke(sr);

        // -> last, run after interceptor
        if (list != null && !list.isEmpty()) {
            for (Interceptor it : list) {
                try {
                    it.onMessageOut(ctx, sp);
                }catch (InterceptorException ie) {
                    throw ie;
                } catch (Throwable t) {
                    throw new InterceptorException(it.getClass().getName(), t);
                }
            }
        }

        // -> finally, return response
        return sp;
    }

    Object doInvoke(Object service, Method method, Object[] args) throws Throwable {
        //ADD add openx proxy service invoke! cool!
        if (Reflector.hasSpring()) {
            if (service instanceof org.springframework.beans.factory.FactoryBean) {
                service = ((org.springframework.beans.factory.FactoryBean) service).getObject();
            }
        }
        if (service instanceof Proxy) {
            Proxy proxy = (Proxy) service;
            InvocationHandler handler = (InvocationHandler) Reflector.getValue(proxy, "h");
            return handler.invoke(proxy, method, args);
        } else {
            try {
                return method.invoke(service, args);
            } catch (InvocationTargetException te) {
                throw new ServiceException(te.getTargetException());
            }
        }
    }

    protected abstract ServiceResponse doInvoke(ServiceRequest sr) throws Throwable;

    protected abstract List<Interceptor> getInterceptor(String mapping);
}